Struts
Apache Struts is a free open-source framework for creating Java web applications.
Web applications differ from conventional websites in that web applications can create a dynamic response. Many websites deliver only static pages. A web application can interact with databases and business logic engines to customize a response.
Web applications based on JavaServer Pages sometimes commingle database code, page design code, and control flow code. In practice, we find that unless these concerns are separated, larger applications become difficult to maintain.
One way to separate concerns in a software application is to use a Model-View-Controller (MVC) architecture. The Model represents the business or database code, the View represents the page design code, and the Controller represents the navigational code. The Struts framework is designed to help developers create web applications that utilize a MVC architecture.
The framework provides three key components:
The framework's architecture and tags are buzzword
compliant. Struts works well with conventional REST applications and with
nouveau technologies like SOAP and
The Apache Struts Project is the open source community that creates and maintains the Apache Struts framework. The project consists of a diverse group of volunteers who share common values regarding collaborative, community-based open source development. The Apache Struts Project is proud to share these values with our parent organization: The Apache Software Foundation. The project is called "Struts" because the framework is meant to furnish the "invisible underpinnings" that support professional application development. Struts provides the glue that joins the various elements of the standard Java platform into a coherent whole. Our goal is to leverage existing standards by producing the missing pieces we need to create enterprise-grade applications that are easy to maintain over time.
The Apache Struts Project offers two major versions of the Struts framework. Struts 1 is recognized as the most popular web application framework for Java. The 1.x framework is mature, well-documented, and widely supported. Struts 1 is the best choice for teams who value proven solutions to common problems.
Struts 2 was originally known as WebWork 2. After working independently for several years, the WebWork and Struts communities joined forces to create Struts 2. The 2.x framework is the best choice for teams who value elegant solutions to difficult problems.
If you are new to development with Struts, here are some pointers on how to get started.
The framework "stands on the shoulders of giants". To use Struts well, it's important to have a good grasp of the fundamentals. Start by reviewing the Key Technologies primer, and studying any unfamiliar topics.
Next, branch to either the Struts 2 or Struts 1 documentation
The Struts 2 documentation is maintained as a wiki, but don't let that stop you from scrolling through. We have organized the wiki so that it can be read like a book, cover to cover. Just keep following the Next links.
Better yet, turn first to the Starting with Struts2 book. At 122 pages, it's a small book that doesn't try to replicate the online Struts 2 Documentation. Instead, the book is a perfect complement to the Struts 2 website. The "minibook" is available both as a free PDF and in conventional printed form.
For Struts 1, the most up-to-date book would be Struts: The Complete Reference. For newbies, Struts for Dummies is another good choice.
And don't hesitate to get involved. The best way to help with any open source project is to improve the documentation! There are mountains of Struts know-how posted to the user mailing list that could be sholved into the documentation. (How do you think this section started?)
My notes: If you download struts youll have a struts-blank.war file
in the distribution. Copy and rename it
to something else and put it in its own directory. You can unzip it to see the various files in
the web app. These include source
files. If you drop this war or directory
into your Tomcat/webapps it will deploy.
Theres not a lot there but it looks like this (selecting Spanish
displays the same in Spanish):
Much of the
Coreservlets example (shown later) is already here. Here is the Welcome page:
Clicking sign in gives:
Note: the struts-actions lib directory needs apache
commons jar files (beanutils.commons and digester.ruleset)
Flow of
control:
The user requests
a form
For now, we use normal HTML to build the form
Later we will use the Struts html:form tag
The form
is submitted to a URL of the form blah.do.
That address is mapped by struts-config.xml to an Action class
The
execute method of the Action object is invoked
One of the arguments to execute is a form bean that is
automatically
created and whose properties are
automatically populated with the
incoming form data
The Action object then invokes business logic and data-access
logic,
placing the results in normal beans stored
in request, session, or application
scope.
The Action uses mapping.findForward to return a condition, and the
conditions are mapped by struts-config.xml
to various JSP pages.
Struts
forwards request to the appropriate JSP page
The page can use bean:write or the JSP 2.0 EL to output bean
properties
The Six
Basic Steps in Using
Struts
1. Modify
struts-config.xml.
Use
WEB-INF/struts-config.xml to:
Map incoming .do addresses to Action classes
Map return conditions to JSP pages
Declare any form beans that are being used.
Be sure to restart the server after modifyingstruts-config.xml; the file is read only when the Web
application is first loaded.
2. Define
a form bean.
This bean is a class the extends ActionForm and will represent the
data submitted by the user. It is
automatically populated when the input form
issubmitted. Beans are postponed until the next section.
3. Create
results beans.
In the MVC architecture, the business-logic and data-access code
create the results and the JSP pages present them. To transfer the
results from one layer to the other, they
are stored in beans. These beans differ from form beans in that they need
extend no particular
class, and they represent the output of
the computational process, not the input to the process. Beans will be
discussed in the next
section.
4. Define an
Action class to handle requests.
The struts-config.xml file designates the Action classes that
handle
requests for various URLs. The Action
objects themselves need to
do the real work: invoke the appropriate
business- and data-accesslogic,
store the results in beans, and designate
the type of situation
(missing data, database error, success
category 1, success category
2, etc.) that is appropriate for the
results. The struts-config.xml file
then decides which JSP page should apply
to that situation.
5. Create
form that invokes blah.do.
Create an input form whose ACTION corresponds to one of the .do
addresses listed in struts-config.xml.
In a later lecture, we will discuss the advantages of using the
Struts
html:form tag to build this input form.
6. Display
results in JSP.
Since Struts is built around MVC, these JSP pages should avoid
JSP scripting elements whenever possible.
For basic Struts, these
pages usually use the bean:write tag, but
in JSP 2.0 the JSP 2.0
expression language is a viable
alternative.
In most cases, the JSP pages only make sense when the request is
funneled through the Action, so the pages
go in WEB-INF.
If the JSP pages makes sense independently of the Action (e.g., if
they display session data), then the JSP
pages should be placed in a
regular subdirectory of the Web
application, and the forward
entries in struts-config.xml should say
Example
1: One Result Mapping
URL
http://hostname/struts-actions/register1.do
Action
Class
RegisterAction1
RegisterAction1 extends Action and is in the coreservlets
package.
The execute method of RegisterAction1 always returns
"success"
Results
page
/WEB-INF/results/confirm.jsp
Step 1A
(Modify struts-config.xml)
Map
incoming .do addresses to Action classes
In this case, we designate that RegisterAction1 should
handle requests for register1.do. To
accomplish this, we
add an action entry to
action-mappings, where action has
the following attributes.
path: the
relative path that should be mapped to the Action,
minus the .do extension. Thus,
path="/register1" refers to
http://hostname/webAppName/register1.do.
type: the
fully qualified class name of the Action class that
should be invoked when a request for the
path is received.
<action-mappings>
<!-- .do
implied automatically -->
<action
path="/register1"
type="coreservlets.RegisterAction1">
...
</action>
Step 1B
(Modify struts-config.xml)
Map
return conditions to JSP pages
In this case, we use the forward element to say that
confirm.jsp applies when the execute
method of
RegisterAction1 returns
"success", as follows:
<forward
name="success"
path="/WEB-INF/results/confirm.jsp"/>
Note: if the same forward is used by multiple actions, you
can put the forward declaration in a
global-forwards
section (before action-mappings) instead
of in the action.
<global-forwards>
<forward
name="success"
path="/WEB-INF/results/confirm.jsp"/>
Step 1
(Modify struts-config.xml)
Final
struts-config.xml
<?xml
version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE
struts-config PUBLIC ... >
<struts-config>
<action-mappings>
<action
path="/register1"
type="coreservlets.RegisterAction1">
<forward
name="success"
path="/WEB-INF/results/confirm.jsp"/>
</action>
</action-mappings>
Steps 2
and 3
Define a
form bean.
Beans are postponed until the next section, so this step is
omitted for now.
Create
results beans.
Beans are postponed until the next section, so
this step is omitted for now.
Step 4
(Define an Action Class to
Handle
Requests)
Action
subclasses should
be in a package.
In this case, we have
package
coreservlets;
This means that the class file should go in your_web_app/WEBINF/
classes/coreservlets/.
Action
subclasses should
add Struts-specific
import statements
to whatever imports are
otherwise
needed.
In this case, we have
import javax.servlet.http.*;
Step 4
(Define an Action Class to
Handle
Requests)
Action
subclasses should ... extend Action
Action
subclasses should ... override execute
In this case, we have
Action
subclasses should ... return
mapping.findForward.
The execute method should have one or more return
values.
These values will then be mapped to specific JSP pages
by forward entries in struts-config.xml.
In this case, we
simply return "success" in all
situations.
package
coreservlets;
imports
public
ActionForward
execute(ActionMapping
mapping,
ActionForm form,
HttpServletRequest
request,
HttpServletResponse
response)
throws Exception
{
Step 5
(Create form that invokes
blah.do)
We need an HTML form that invokes
http://hostname/struts-actions/register1.do.
Step 6
(Display results in JSP)
In general,
there can be several possible JSP
pages
Corresponding to the various possible return values of the
execute method of the Action.
In
struts-config.xml, each JSP page is declared
in a
forward entry within the appropriate action.
In this simple case, the only return value is "success",
so
/WEB-INF/results/confirm.jsp is used in
all cases.
This JSP page will just display a simple message
<html>
You have successfully register
Step 1
(Modify struts-config.xml)
Final Code
<?xml version="1.0"
encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
... >
<struts-config>
<action-mappings>
...
<action
path="/register2"
type="coreservlets.RegisterAction2">
<forward
name="bad-address"
path="/WEB-INF/results/bad-address.jsp"/>
<forward
name="bad-password"
path="/WEB-INF/results/bad-password.jsp"/>
<forward
name="success"
path="/WEB-INF/results/confirm.jsp"/>
</action>
</action-mappings>
</struts-config>
Steps 2
and 3
Define a
form bean.
Beans are postponed until the next section, so this step is
omitted for now.
Create
results beans.
Beans are postponed until the next section, so this step is
omitted for now.
Step 4
(Define an Action Class to
Handle
Requests)
Similar
to the previous example except for
multiple
mapping.findForward entries
We return "bad-address" if the email address is missing,
is less then three characters long, or
does not contain an
"@" sign.
We return "bad-password" if the password is missing or
is less than six characters long.
Otherwise we return "success".
In this
simple example we use
request.getParameter
explicitly.
In later examples we let Struts automatically populate a
bean from the request data.
4
(Define an Action Class to
Handle
Requests) Final Code
public class RegisterAction2
extends Action {
public
ActionForward
execute(ActionMapping
mapping,
ActionForm form,
HttpServletRequest
request,
HttpServletResponse
response)
throws Exception
{
String email =
request.getParameter("email");
String password
= request.getParameter("password");
if ((email ==
null) ||
(email.trim().length()
< 3) ||
(email.indexOf("@")
== -1)) {
return(mapping.findForward("bad-address"));
} else if
((password == null) ||
(password.trim().length()
< 6)) {
return(mapping.findForward("bad-password"));
} else {
return(mapping.findForward("success"));
}}}
Step 5
(Create Form that Invokes
blah.do)
We need
an HTML form that invokes
http://hostname/struts-actions/register2.do.
<!DOCTYPE
...>
<HTML>
<HEAD><TITLE>New
Account Registration</TITLE></HEAD>
<BODY
BGCOLOR="#FDF5E6">
<CENTER>
<H1>New
Account Registration</H1>
<FORM ACTION="register2.do"
METHOD="POST">
EMAIL ADDRESS
<INPUT TYPE="TEXT" name=Email></br>
PASSWORD
<input type="PASSWORD" name=password></br>
<INPUT
TYPE="SUBMIT" VALUE="Sign Me Up!">
</FORM>
</CENTER>
</BODY></HTML>
Step 6
(Display results in JSP)
First
Possible Page
<!DOCTYPE
...>
<HTML>
<HEAD><TITLE>Illegal
Email Address</TITLE></HEAD>
<BODY
BGCOLOR="#FDF5E6">
<CENTER>
<H1>Illegal
Email Address</H1>
Address must be
of the form username@host.
Please <A
HREF="register2.jsp">
try
again</A>.
</CENTER>
</BODY></HTML>
Step 6
(Display results in JSP)
Second Possible Page
<!DOCTYPE ...>
<HTML>
<HEAD><TITLE>Illegal
Password</TITLE></HEAD>
<BODY
BGCOLOR="#FDF5E6">
<CENTER>
<H1>Illegal
Password</H1>
Password must
contain at least six characters.
Please <A
HREF="register2.jsp">
try
again</A>.
</CENTER>
</BODY></HTML>
Step 6
(Display results in JSP)
Same
confirm.jsp Shown Earlier
<!DOCTYPE
...>
<HTML>
<HEAD><TITLE>Success</TITLE></HEAD>
<BODY
BGCOLOR="#FDF5E6">
<CENTER>
<H1>You
have registered successfully.</H1>
Congratulations
</CENTER>
</BODY></HTML>
Combining
Shared Condition
(Forward)
Mappings
Idea
If the same condition is mapped to the same JSP page in
multiple actions, you can move the forward
to a globalforwards
section to avoid repetition
Syntax
The global-forwards section goes before
action-mappings, not within it
The forward entries within global-forwards have the same
syntax and behavior as forward entries
within action
Example
<global-forwards>
<forward
name="success"
path="/WEB-INF/results/confirm.jsp"/>
</global-forwards>
CoreServle
Combining Shared Condition
(Forward)
Mappings: Old
<action-mappings>
<action
path="/register1"
type="coreservlets.RegisterAction1">
<forward
name="success"
path="/WEB-INF/results/confirm.jsp"/>
</action>
<action
path="/register2"
type="coreservlets.RegisterAction2">
<forward
name="bad-address"
path="/WEB-INF/results/bad-address.jsp"/>
<forward
name="bad-password"
path="/WEB-INF/results/bad-password.jsp"/>
<forward
name="success"
path="/WEB-INF/results/confirm.jsp"/>
</action>
...
Combining
Shared Condition
(Forward)
Mappings: New
<global-forwards>
<forward
name="success"
path="/WEB-INF/results/confirm.jsp"/>
</global-forwards>
<action-mappings>
<action
path="/register1"
type="coreservlets.RegisterAction1">
</action>
<action
path="/register2"
type="coreservlets.RegisterAction2">
<forward
name="bad-address"
path="/WEB-INF/results/bad-address.jsp"/>
<forward name="bad-password"
path="/WEB-INF/results/bad-password.jsp"/>
</action>
...
Summary
Modify
struts-config.xml
Map blah.do addresses to subclasses of Action
Map return conditions (from execute) to JSP pages
Declare any form beans that are being used.
Define
a form bean
Create
results beans
Define an
Action class to handle requests
Extend Action
Override execute
Return mapping.findForward
Create
form that invokes blah.do
From a RoseIndia Struts tutorial
What is Struts?
Struts Frame work is the implementation of Model-View-Controller (MVC) design pattern for the JSP. Struts is maintained as a part of Apache Jakarta project and is open source. Struts Framework is suited for the application of any size. Latest version of struts can be downloaded from http://jakarta.apache.org/. We are using jakarta-struts-1.1 and jakarta-tomcat-5.0.4 for this tutorial.
What is Model-View-Controller (MVC) Architecture?
Model-View-Controller architecture is all about dividing application components into three different categories Model, View and the Controller. Components of the MVC architecture has unique responsibility and each component is independent of the other component. Changes in one component will have no or less impact on other component. Responsibilities of the components are:
Model: Model is responsible for providing the data from the database and saving the data into the data store. All the business logic is implemented in the Model. Data entered by the user through View are check in the model before saving into the database. Data access, Data validation and the data saving logic are part of Model. It is possible to reuse the same model for many page requests. Struts provides the ActionForm and the Action classes which can be extended to create the model objects.
View: View represents the user view of the application and is responsible for taking the input from the user, dispatching the request to the controller and then receiving response from the controller and displaying the result to the user. HTML, JSPs, Custom Tag Libraries and Resources files are the part of view component. The view in the struts framework is mainly a jsp page which is responsible for producing the output to the user.
Controller: Controller is intermediary between Model and View. Controller is responsible for receiving the request from client. Once request is received from client it executes the appropriate business logic from the Model and then produce the output to the user using the View component. ActionServlet, Action, ActionForm and struts-config.xml are the part of Controller. Most Struts application will have only one controller that is ActionServlet which is responsible for directing several Actions. The controller determines what action is required and sends the information to be processed by an action Bean. The key advantage of having a controller is its ability to control the flow of logic through the highly controlled, centralized points.
Each application we develop has a deployment descriptor i.e. WEB-INF/web.xml.
This file has all the configuration information
which we have defined for our web application. The configuration information
includes the index file, the default welcome page, the mapping of our servlets
including path and the extension name, any init parameters, information related
to the context elements.
In the file WEB-INF/web.xml of struts application we need to configure the
Struts ActionServlet which handles all the request made by the web browsers to
a given mapping. ActionServlet is the central component of the Struts
controller. This servlet extends the HttpServlet. This servlet basically
performs two important things. First is : When the container gets start,
it reads the Struts Configuration files and loads it into memory in the init()
method. You will know more about the Struts Configuration files below. Second
point is: It intercepts the HTTP request in the doGet() and doPost()
method and handles it appropriately.
Note: The points we have described above will be in effect if and only if when the ActionServlet is handling the request. When the request is submitted to the container which call the ActionServlet, make sure that the extension of the file which we want to access should have the extension .do.
Struts working:
Process flow:
web.xml : Whenever the container gets start up the first work it does is to check the web.xml file and determine what struts action Servlets exist. The container is responsible for mapping all the file request to the correct action Servlet.
A Request : This is the second step performed by the container after checking the web.xml file. In this the user submits a form within a browser and the request is intercepted by the controller.
struts.config.xml : Struts has a configuration file to store mappings of actions. By using this file there is no need to hard code the module which will be called within a component. The one more responsibility of the controller is to check the struts.config.xml file to determine which module to be called upon an action request. Struts only reads the struts.config.xml file upon start up.
Struts tag libraries : These are struts components helps us to integrate the struts framework within the project's logic. These struts tag libraries are used within the JSP page. This means that the controller and the model part can't make use of the tag library but instead use the struts class library for strut process control.
Property file : It is used to store the messages that an object or page can use. Properties files can be used to store the titles and other string data. We can create many property files to handle different languages.
Business objects : It is the place where the rules of the actual project exists. These are the modules which just regulate the day- to- day site activities.
The Response :
This is the output of the View JSP object.
In this section I will describe you the Controller part of the Struts Framework. I will show you how to configure the struts-config.xml file to map the request to some destination servlet or jsp file.
The class org.apache.struts.action.ActionServlet is the heart of the Struts Framework. It is the Controller part of the Struts Framework. ActionServlet is configured as Servlet in the web.xml file as shown in the following code snippets.
<!-- Standard Action Servlet Configuration
(with debugging) -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
This servlet is responsible for handing all the requests for the Struts Framework, user can map the specific pattern of request to the ActionServlet. <servlet-mapping> tag in the web.xml file specifies the url pattern to be handled by the servlet. By default it is *.do, but it can be changed to anything. This piece of web.xml file shows the mapping:
<!-- Standard Action Servlet
Mapping --> |
The above mapping maps all the requests ending with
.do to the ActionServlet. ActionServlet uses the configuration defined
in struts-config.xml file to decide the destination of the request. Action
Mapping Definitions (described below) is used to map any action. For this
lesson we will create Welcome.jsp file and map the "Welcome.do"
request to this page.
Welcome.jsp
<%@ taglib uri="/tags/struts-bean"
prefix="bean" %> |
Forwarding the Welcome.do request to Welcome.jsp
The "Action Mapping Definitions" is the most important part in the struts-config.xml. This section takes a form defined in the "Form Bean Definitions" section and maps it to an action class.
Following code under the <action-mappings> tag is used to forward the request to the Welcome.jsp.
<action path="/Welcome" |
To call this Welcome.jsp file we will use the following code.
<html:link page="/Welcome.do">First Request to the controller</html:link> |
Once the use clicks on on First Request to the controller link on the index page, request (for Welcome.do) is sent to the Controller and the controller forwards the request to Welcome.jsp. The content of Welcome.jsp is displayed to the user.
Setting Up Development Environment
You need java and Tomcat to proceed.
Installing Struts Application:
Download latest version of Struts from the official site of Struts http://jakarta.apache.org/struts. Extract the file to your favorite directory and copy struts-blank.war, struts-documentation.war and struts-example.war from "jakarta-struts-1.1\webapps" directory into "jakarta-tomcat-5.0.4\webapps" directory.
struts-blank.war is the blank struts application which is useful in creating struts application from scratch. We will use this file to create our web application.
struts-documentation.war contains API and important documents for the struts application development.
struts-example.war is a simple MailReader Demonstration Application.
Developing First Struts Application
Rename struts-blank.war to struts-tutorial.war from jakarta-tomcat-5.0.4\webapps and copy it to the "jakarta-tomcat-5.0.4\webapps" directory. Tomcat automatically extracts the file and loads the application.
What is Action Class?
An Action class in the struts application extends Struts 'org.apache.struts.action.Action" Class. Action class acts as wrapper around the business logic and provides an inteface to the application's Model layer. It acts as glue between the View and Model layer. It also transfers the data from the view layer to the specific business process layer and finally returns the procssed data from business layer to the view layer.
An Action works as an adapter between the contents of an incoming HTTP request and the business logic that corresponds to it. Then the struts controller (ActionServlet) slects an appropriate Action and creates an instance if necessary, and finally calls execute method.
To use the Action, we need to Subclass and overwrite the execute() method. In the Action Class don't add the business process logic, instead move the database and business process logic to the process or dao layer.
The ActionServlet (commad) passes the parameterized class to Action Form using the execute() method. The return type of the execute method is ActionForward which is used by the Struts Framework to forward the request to the file as per the value of the returned ActionForward object.
Developing our Action Class?
Our Action class (TestAction.java) is a simple class that forwards the TestAction.jsp. Our Action class returns the ActionForward called "testAction", which is defined in the struts-config.xml file (action mapping is show later in this page). Here is code of our Action Class:
TestAction.java
|
Understanding Action Class
Here is the signature of the Action Class.
public ActionForward execute(ActionMapping mapping,
ActionForm form,
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws java.lang.Exception
Action Class processes the specified HTTP request, and
creates the corresponding HTTP response (or forwards to another web component
that will create it), with provision for handling exceptions thrown by the
business logic. Return an ActionForward
instance
describing where and how control should be forwarded, or null
if the response has already been completed.
Parameters:
mapping
- The ActionMapping used to select this instance
form
- The optional ActionForm bean for this request (if any)
request
- The HTTP request we are processing
response
- The HTTP response we are creating
Throws:
Action class throws java.lang.Exception
- if the application business logic throws an exception
Adding the Action Mapping in the struts-config.xml
To test the application we will add a link
in the index.jsp
<html:link page="/TestAction.do">Test the
Action</html:link>
Following code under the <action-mappings> tag is used for mapping the TestAction class.
<action path="/TestAction" type="TestAction"> <forward name="testAction" path="/pages/TestAction.jsp"/> </action> |
To test the new application click on Test the Action link on the index page. The content of TestAction.jsp should be displayed on the user browser.
A Struts CRUD example
http://www.learntechnology.net/content/struts/struts_crud.jsp
This is a complete example which builds the
application shown below. It does not use
a DB, but just allocates (hardcodes) a List of employees. All the files in the app are not shown in the
tutorial although I have somewhat augmented that here, pasting in some more
of the original files - but they all do come in the source download. War files can also be downloaded. Source has
an ant script which makes it easy to work on.
Here is the project (looks like he used netbeans but I
just used textpad to edit the java and blackscreen/ant to build war file.)
Here is employeeAction below- from net.reumann.demo.action package which is referenced in the config file: (You would need a DepartmentsAction class to add CRUD to departments table)
package net.reumann.demo.action;
import net.reumann.demo.Constants;
import net.reumann.demo.form.EmployeeForm;
import net.reumann.demo.service.DepartmentService;
import net.reumann.demo.service.EmployeeService;
import net.reumann.demo.service.DepartmentDaoService;
import net.reumann.demo.service.EmployeeDaoService;
import net.reumann.demo.vo.Employee;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.*;
import org.apache.struts.actions.DispatchAction;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public class EmployeeAction extends DispatchAction {
private Log logger = LogFactory.getLog(this.getClass());
private static EmployeeService empService = new EmployeeDaoService();
private static DepartmentService deptService = new DepartmentDaoService();
public ActionForward getEmployees(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.debug("getEmployees");
populateEmployees(request);
return mapping.findForward(Constants.SUCCESS);
}
public ActionForward setUpForInsertOrUpdate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.debug("setUpForInsertOrUpdate");
EmployeeForm employeeForm = (EmployeeForm)form;
if (isUpdate(request, employeeForm)) {
Integer id = Integer.valueOf(employeeForm.getEmployeeId());
Employee employee = empService.getEmployee(id);
BeanUtils.copyProperties(employeeForm, employee);
}
prep(request);
return mapping.findForward(Constants.SUCCESS);
}
public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.debug("delete");
EmployeeForm employeeForm = (EmployeeForm)form;
Integer id = Integer.valueOf(employeeForm.getEmployeeId());
empService.deleteEmployee(id);
populateEmployees(request);
return mapping.findForward(Constants.SUCCESS);
}
public ActionForward insertOrUpdate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.debug("insertOrUpdate");
EmployeeForm employeeForm = (EmployeeForm)form;
if (validationSuccessful(request, employeeForm)) {
Employee employee = new Employee();
BeanUtils.copyProperties(employee, employeeForm);
if (isUpdate(request, employeeForm)) {
logger.debug("update");
empService.updateEmployee(employee);
} else {
logger.debug("insert" );
empService.insertEmployee(employee);
}
populateEmployees(request);
return mapping.findForward(Constants.SUCCESS);
} else {
prep(request);
return mapping.findForward(Constants.FAILURE);
}
}
private void populateEmployees(HttpServletRequest request) {
List employees = empService.getAllEmployees();
request.setAttribute(Constants.EMPLOYEES, employees);
prep(request);
}
private void prep(HttpServletRequest request) {
request.setAttribute(Constants.DEPARTMENTS, deptService.getAllDepartments());
}
private boolean isUpdate(HttpServletRequest request, EmployeeForm empForm) {
boolean updateFlag = true;
//if ID is null or 0 we know we are doing an insert. You could check other
//things to decide, like a dispatch param
//It's annoying that BeanUtils will convert nulls to 0 so have to do 0 check also,
//or you could register a converter, which is the preferred way to handle it, but goes
//beyond this demo
String id = empForm.getEmployeeId();
if (id == null || id.trim().length() == 0 || Integer.parseInt(id) == 0) {
updateFlag = false;
}
request.setAttribute("updateFlag", Boolean.valueOf(updateFlag));
return updateFlag;
}
private boolean validationSuccessful(HttpServletRequest request, EmployeeForm form) {
//if you really like using the validation framework stuff, you can just
//call ActionErrors errors = form.validate( mapping, request ); in this method
//and check for errors being empty, if not save them and you're done.
//I end up finding the validation framework a bit annoying to work with, so I do it
//old-Skool way. Inevitably in a more complex app you end up having to perform
//more complex validation than the validation framework provides, so I just assume
//keep it all here in one place, versus having some handled by xml configuration and
//some hardcoded.
boolean isOk = true;
ActionMessages errors = new ActionMessages();
if (form.getAge() == null || form.getAge().trim().length() == 0) {
errors.add("age", new ActionMessage("errors.required", "Age"));
} else {
try {
Integer.parseInt(form.getAge());
} catch (NumberFormatException e) {
errors.add("age", new ActionMessage("errors.number", "Age"));
}
}
if (form.getFirstName() == null || form.getFirstName().trim().length() == 0) {
errors.add("firstName", new ActionMessage("errors.required", "First Name"));
}
if (form.getLastName() == null || form.getLastName().trim().length() == 0) {
errors.add("lastName", new ActionMessage("errors.required", "Last Name"));
}
if (!errors.isEmpty()) {
saveErrors(request, errors);
isOk = false;
}
return isOk;
}
}
Original example code ( to fit this
lesson) and the one shown above:
rr-struts-crud source code
rr-struts-crud.war
This example comes with an ant
build. For CRUD: Add db tables for
departments and employees and populate lists from these.
Higgins noteI used above source
files, not the one below with a nested class below.
NOTE: This lesson (and the above source
and war) was written using an Employee object that contained a
"departmentId." It makes more sense to work with a Department object
nested inside of the Employee object, so I've provided updated source code and
a war to reflect this change. I haven't gotten around to updating this lesson
to use this latter design (any volunteers much appreicated:). The new source
and war can be downloaded below:
rr-struts-crud2
source code
rr-struts-crud2.war
Introduction
Assumes some previous knowledge of
Struts. This download application demonstrates a very simple application that
handles your typical CRUD - Create, Retrieve, Update, Delete operations. I've
noticed many people understand the basics of struts but have a difficult time
putting together all the pieces. Hopefully this application will serve as a
decent guide.
Requirements
This application requires an
application server that implements the Servlet 2.4 and JavaServer Pages 2.0
specifications. The examples should all work on Tomcat 5.x (Discussed in next
section). Please do not e-mail about getting your application to run on a
server other than Tomcat. The source code (and an Ant build file) is provided
for all the lessons so you should be able to build a war from the source and
run it on you application server of choice.
Jars
This application uses the following
jars:
You can just use the jars that come with
the source code for this application, but if you want the latest versions:
The latest commons jar files commons-beanutils, commons-digester,
commons-logging can be found here http://jakarta.apache.org/commons/,
but it's esaier to just download the latest Struts Action Framwork http://struts.apache.org/acquiring.html
and just use the commons jars that are in the example apps provided with Struts.
Obviously you should get the latest struts jar from there as well.
JSTL jars (standard, jstl) http://cvs.apache.org/builds/jakarta-taglibs/nightly/
log4j http://logging.apache.org/log4j/docs/download.html
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<display-name>Rick Reumann Struts-CRUD Demo</display-name>
<description/>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error.jsp</location>
</error-page>
<context-param>
<param-name>
javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>MessageResources</param-value>
</context-param>
</web-app>
I like to define a global error.jsp
in my web.xml that all my errors from the application will trickle up to. You
could also define this in the struts-config but I prefer defining it in
web.xml.
Notice the definition of the MessageResources file in the web.xml. Instead of
using the old bean:write tag to display messages from our resources file, we're
using the JSTL format tag. In order to use this tag the message bundle needs to
be defined in the web.xml (*We still need to define this Resources file in the
struts-config file so that errors and messages can be set up). If you look at
the actual MessageResources file in the src directory you'll see it is actually
called "MessageResources_en.properties." This is nice since you can
provide different Locale resource files for different languages _it (Italian),
_de (German), _fr (French), etc.
struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<form-beans>
<form-bean name="employeeForm"
type="net.reumann.demo.form.EmployeeForm"/>
</form-beans>
<action-mappings>
<action
path="/employeeSetUp"
name="employeeForm"
type="net.reumann.demo.action.EmployeeAction"
scope="request"
parameter="dispatch">
<forward name="success" path="/employeeForm.jsp"/>
</action>
<action
path="/employeeProcess"
name="employeeForm"
type="net.reumann.demo.action.EmployeeAction"
scope="request"
parameter="dispatch">
<forward name="failure" path="/employeeForm.jsp"/>
<forward name="success" path="/employees.jsp"/>
</action>
</action-mappings>
<message-resources parameter="MessageResources" null="false"/>
</struts-config>
Nothing new and exciting here. I
could have actually just had one mapping for this simple app /employeeProcess,
but since I want the 'success' to go to a different page for setUp I just made
its own mapping. A common mistake people make is that they forget that even if
you use a DispatchAction you can still have multiple mappings that go to the
same DispatchAction.
Added by Higgins---Interface DepartmentDao
notice, that to enable CRUD youd have to add signatures/methods as in employeeDAO and implementing class and service etc shown below.
package net.reumann.demo.persistence;
import java.util.List;
//import java.util.Map;
public interface DepartmentDao {
public List getAllDepartments();
//this not neededΰpublic Map getDepartmentsMap();
}
Interface EmployeeDao
public interface EmployeeDao {
public List getAllEmployees();
public Employee getEmployee(Integer id);
public void update(Employee emp);
public void insert(Employee emp);
public void delete(Integer id);
}
Always a good practice to code to an
Interface.
EmployeeNoDBdao *****this
is one class you need to change to use a db
public class EmployeeNoDBdao implements EmployeeDao {
private static List employees;
static {
employees = new ArrayList();
employees.add(
new Employee(
new Integer(1), "John", "Doe", new Integer(36),
new Integer(100) ) );
employees.add(
new Employee(
new Integer(2), "Bob", "Smith", new Integer(25),
new Integer(300) ) );
}
Log logger = LogFactory.getLog(this.getClass());
public List getAllEmployees() {
return employees;
}
public Employee getEmployee(Integer id) {
Employee emp = null;
Iterator iter = employees.iterator();
while( iter.hasNext() ) {
emp = (Employee)iter.next();
if ( emp.getEmployeeId().equals( id ) ) {
break;
}
}
return emp;
}
public void update(Employee emp) {
Integer id = emp.getEmployeeId();
for(int i = 0; i < employees.size(); i++ ) {
Employee tempEmp = (Employee)employees.get(i);
if ( tempEmp.getEmployeeId().equals( id ) ) {
employees.set( i, emp );
break;
}
}
}
public void insert(Employee emp) {
int lastId = 0;
Iterator iter = employees.iterator();
while( iter.hasNext() ) {
Employee temp = (Employee)iter.next();
if ( temp.getEmployeeId().intValue() > lastId ) {
lastId = temp.getEmployeeId().intValue();
}
}
emp.setEmployeeId(new Integer(lastId + 1));
employees.add(emp);
}
public void delete(Integer id) {
for(int i = 0; i < employees.size(); i++ ) {
Employee tempEmp = (Employee)employees.get(i);
if ( tempEmp.getEmployeeId().equals( id ) ) {
employees.remove( i );
break;
}
}
}
}
There is no database or other
persistence, we're just updating a static List of employees.
EmployeeService
public interface EmployeeService {
public List getAllEmployees();
public void updateEmployee(Employee emp);
public void deleteEmployee(Integer id);
public Employee getEmployee(Integer id);
public void insertEmployee(Employee emp);
}
Typical interface for our Service
class..
EmployeeDaoService
public class EmployeeDaoService implements EmployeeService {
private EmployeeDao dao;
public EmployeeDaoService() {
this.dao = new EmployeeNoDBdao();//change this to your new db-enabled class
}
public List getAllEmployees() {
return dao.getAllEmployees();
}
public void updateEmployee(Employee emp) {
dao.update(emp);
}
public void deleteEmployee(Integer id) {
dao.delete(id);
}
public Employee getEmployee(Integer id) {
return dao.getEmployee(id);
}
public void insertEmployee(Employee emp) {
dao.insert(emp);
}
}
An implementation of our
EmployeeService that we are using. Notice the constructor initializes our dao.
If you were going to have different types of DAOs that could be used, you'd
want to code something more flexible - like having a factory return you the
proper dao. Or better yet, just use Spring which will inject the correct DAO
for you based on what you define in a simple xml file. You might be wondering
why we are even bothering with a Service class when it doesn't do anything but
simply call our DAO methods. Why not just use our DAO object directly from the
Action class and forget about this whole Service class? The reason I like to
have this extra layer is that in a real-life more complex application there are
often other business operations you might want to perform besides just calling
you DAO. For example, maybe when an "update" is doine you need to
call some process that sends out an e-mail or some kind of notification. If you
don't use a Service class you are stuck now between coding this business logic
either in your Action class or in the DAO. Neither of those places is really a
good place for that kind of logic - hence we provide an extra service class to
handle business rules that shouldn't be in the Action and don't belong in a
DAO. Of course for rapid development, you could possibly skip the Service
classes and just use the DAOs directly within your Action.
EmployeeActionΰhiggins note
youd add a
DepartmentAction to handle Department table CRUD
public class EmployeeAction extends DispatchAction {
private Log logger = LogFactory.getLog(this.getClass());
private static EmployeeService empService =
new EmployeeDaoService();
private static DepartmentService deptService =
new DepartmentDaoService();
public ActionForward getEmployees(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {
logger.debug("getEmployees");
populateEmployees(request);
return mapping.findForward(Constants.SUCCESS);
}
public ActionForward setUpForInsertOrUpdate(ActionMapping mapping,
ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
logger.debug("setUpForInsertOrUpdate");
EmployeeForm employeeForm = (EmployeeForm)form;
if (isUpdate(request, employeeForm)) {
Integer id = Integer.valueOf(employeeForm.getEmployeeId());
Employee employee = empService.getEmployee(id);
BeanUtils.copyProperties(employeeForm, employee);
}
prep(request);
return mapping.findForward(Constants.SUCCESS);
}
public ActionForward delete(ActionMapping mapping,
ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
logger.debug("delete");
EmployeeForm employeeForm = (EmployeeForm)form;
Integer id = Integer.valueOf(employeeForm.getEmployeeId());
empService.deleteEmployee(id);
populateEmployees(request);
return mapping.findForward(Constants.SUCCESS);
}
public ActionForward insertOrUpdate(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {
logger.debug("insertOrUpdate");
EmployeeForm employeeForm = (EmployeeForm)form;
if (validationSuccessful(request, employeeForm)) {
Employee employee = new Employee();
BeanUtils.copyProperties(employee, employeeForm);
if (isUpdate(request, employeeForm)) {
logger.debug("update");
empService.updateEmployee(employee);
} else {
logger.debug("insert" );
empService.insertEmployee(employee);
}
populateEmployees(request);
return mapping.findForward(Constants.SUCCESS);
} else {
prep(request);
return mapping.findForward(Constants.FAILURE);
}
}
private void populateEmployees(HttpServletRequest request) {
List employees = empService.getAllEmployees();
request.setAttribute(Constants.EMPLOYEES, employees);
prep(request);
}
private void prep(HttpServletRequest request) {
request.setAttribute(Constants.DEPARTMENTS,
deptService.getAllDepartments());
}
private boolean isUpdate(HttpServletRequest request,
EmployeeForm empForm) {
boolean updateFlag = true;
String id = empForm.getEmployeeId();
if (id == null || id.trim().length() == 0 ||
Integer.parseInt(id) == 0) {
updateFlag = false;
}
request.setAttribute("updateFlag", Boolean.valueOf(updateFlag));
return updateFlag;
}
private boolean validationSuccessful(HttpServletRequest request,
EmployeeForm form) {
boolean isOk = true;
ActionMessages errors = new ActionMessages();
if (form.getAge() == null ||
form.getAge().trim().length() == 0) {
errors.add("age", new ActionMessage("errors.required", "Age"));
} else {
try {
Integer.parseInt(form.getAge());
} catch (NumberFormatException e) {
errors.add("age", new ActionMessage("errors.number", "Age"));
}
}
if (form.getFirstName() == null ||
form.getFirstName().trim().length() == 0) {
errors.add("firstName",
new ActionMessage("errors.required", "First Name"));
}
if (form.getLastName() == null ||
form.getLastName().trim().length() == 0) {
errors.add("lastName",
new ActionMessage("errors.required", "Last Name"));
}
if (!errors.isEmpty()) {
saveErrors(request, errors);
isOk = false;
}
return isOk;
}
}
I like using standard
DispatchActions. Using a DispatchAction you can keep related functionality in
one class. This class is our 'controller' for our Employee related tasks.
if (validationSuccessful(request, employeeForm))
returns false the
"prep(request)" method is called. In this example prep makes
sure to put my Departments list in Request scope. Of course if you were
certain that the list of departments was never going to change you could
use Application scope, in which case you could set that list in scope
somewhere else, but I'm simply demonstrating here how to easily make sure
your Lists stay in Request scope even when validation fails. (Session
would also work, but I think the Session is a bad place to store form
Lists - you're basically adding extra overhead for no gain.) If you want to use the validation
framework and configure your validation rules in an xml file you could still
use the approach above where you manually call validate. Thus the
validationSuccessful method could be shortened to:
private boolean validationSuccessful(HttpServletRequest request,
EmployeeForm form) {
ActionMessages errors = form.validate();
if (!errors.isEmpty()) {
saveErrors(request, errors);
return false;
} else {
return true;
}
}
I tend to not use the validation framework
to handle my validation because inevitably I end up with some complex
validation that isn't easily handled by an xml configuration, so since I end up
having to write up some validation code, I find it easier to have all of it one
place versus some in a config file and some custom in a validation method. If
you validation needs are definitely going to be simple, I'd use the validation
framework and call the forms validate method manually as just described.
employee.jsp
<html>
<head>
<link href="<c:url value='main.css'/>"
rel="stylesheet" type="text/css"/>
<title><fmt:message key="label.employees"/></title>
</head>
<body>
<div class="titleDiv"><fmt:message key="application.title"/></div>
<h1><fmt:message key="label.employees"/></h1>
<c:url var="url" scope="page" value="/employeeSetUp.do">
<c:param name="dispatch" value="setUpForInsertOrUpdate"/>
</c:url>
<a href="${url}">Add New Employee</a>
<br/><br/>
<table class="borderAll">
<tr>
<th><fmt:message key="label.firstName"/></th>
<th><fmt:message key="label.lastName"/></th>
<th><fmt:message key="label.age"/></th>
<th><fmt:message key="label.department"/></th>
<th> </th>
</tr>
<c:forEach var="emp" items="${employees}" varStatus="status">
<tr class="${status.index%2==0?'even':'odd'}">
<td class="nowrap"><c:out value="${emp.firstName}"/></td>
<td class="nowrap"><c:out value="${emp.lastName}"/></td>
<td class="nowrap"><c:out value="${emp.age}"/></td>
<td class="nowrap">
<%-- NOTE: this is NOT the best way to get the department name.
Just using this approach just for ease of backend objects --%>
<c:forEach var="dept" items="${departments}">
<c:if test="${dept.departmentId == emp.departmentId}">
<c:out value="${dept.name}"/>
</c:if>
</c:forEach>
</td>
<td class="nowrap">
<c:url var="url" scope="page" value="/employeeSetUp.do">
<c:param name="employeeId" value="${emp.employeeId}"/>
<c:param name="dispatch" value="setUpForInsertOrUpdate"/>
</c:url>
<a href="${url}">Edit</a>
<c:url var="url" scope="page" value="/employeeProcess.do">
<c:param name="employeeId" value="${emp.employeeId}"/>
<c:param name="dispatch" value="delete"/>
</c:url>
<a href="${url}">Delete</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
employeeForm.jsp
<html>
<head>
<link href="<c:url value='main.css'/>" rel="stylesheet" type="text/css"/>
<style>td { white-space:nowrap; }</style>
<title><c:out value="${insertUpdateTitle}"/></title>
</head>
<body>
<div class="titleDiv"><fmt:message key="application.title"/></div>
<h1><c:out value="${insertUpdateTitle}"/></h1>
<html:form action="/employeeProcess">
<table>
<tr>
<td class="tdLabel"><fmt:message key="label.firstName"/>:</td>
<td><html:text property="firstName" size="40"/>
<html:errors property="firstName"/></td>
</tr>
<tr>
<td class="tdLabel"><fmt:message key="label.lastName"/>:</td>
<td><html:text property="lastName" size="40"/>
<html:errors property="lastName"/></td>
</tr>
<tr>
<td class="tdLabel"><fmt:message key="label.age"/>:</td>
<td><html:text property="age" size="20"/>
<html:errors property="age"/></td>
</tr>
<tr>
<td class="tdLabel"><fmt:message key="label.department"/>:</td>
<td>
<html:select property="departmentId">
<c:forEach var="dept" items="${departments}">
<html:option value="${dept.departmentId}">
<c:out value="${dept.name}"/>
</html:option>
</c:forEach>
</html:select>
</td>
</tr>
<tr>
<td colspan="2">
<html:hidden property="employeeId"/>
<input type="hidden" name="dispatch" value="insertOrUpdate"/>
<br/>
<input type="submit" value="<
fmt:message key="button.label.submit"/>" class="butStnd"/>
<input type="submit" value="<
fmt:message key="button.label.cancel"/>" class="butStnd"
onclick="document.employeeForm.dispatch.value='getEmployees'"/>
</td>
</tr>
</table>
</html:form>
</body>
</html>
There is a java file of string constants and app properties not shown here.
I redid the example above with a db connection. It looks like this:
I have only partially implemented CRUD (I did create and delete and read) but only show the populateEmployees here. The interface and jsp are complete and need no changes. The data is loaded from the database (shown below). To do this, you add functionality to the existing java dao to save to db when changes are made.
Here are my db tables (departments)
And employees:
For Employee table CRUD, I only changed two files, and the includes etc and constructor calls etc related to them elsewhere --- some are noted above, in Service classes where the data is created/persisted. The example uses EmployeeNoDBDao and DepartmentNoDBDao. I replaced these with EmployeeWithDBDao and DepartmentWithDBDao. Here, for example, is the constructor for the former,
public EmployeeWithDBdao() {
con = null;
stmt = null;
rs = null;
try{
employees = new ArrayList();
Class.forName("org.gjt.mm.mysql.Driver");
// Get a Connection to the database
con = DriverManager.getConnection( "jdbc:mysql://localhost/strutex", "DMH", "DMH");//
System.out.println("got con...employee dao ");
stmt = con.createStatement();
// Execute an SQL query, get a ResultSet
rs = stmt.executeQuery("SELECT employeeId,FIRSTNAME,LASTNAME, Age,departmentId from Employees");//
System.out.println("executed stmt...employee dao ");
while(rs.next()){
String first=rs.getString("FIRSTNAME");
String last=rs.getString("LASTNAME");
Integer empId=new Integer(rs.getInt("employeeId"));
Integer age=new Integer(rs.getInt("Age"));
Integer deptId=new Integer(rs.getInt("departmentId"));
System.out.println("looping rs...employee dao ");
employees.add( new Employee( empId, first, last, age, deptId ) );
}//while rs has next
}
catch(Exception e){System.out.println("sql prob");}
}//constructor
It is be easy to add similar functionality to this implementation class which updates the database when any changes are made basically the same db stuff weve done before. Heres the (already existing) jsp form to add an employee
Heres the db table after Ive submitted this:
Here Ive deleted Lewis Holmes:
Then, checking the database:
Youll have to check the db and get a screen shot of that to assure that the employee really has been added/changed/deleted or just show me your working application.
Regular exercise: Complete this with all CRUD on Employees.
E.C. exercise: add jsp & java classes to view/edit the Departments table as well and provide CRUD for that. This is non trivial as youll have to mimick employee jsp and java classes for departments, and edit the struts-config.xml also.
For example, here is the departments.jsp:
And the db table
After deleting intl travel:
And in the database:
Adding a new department
Heres the db table showing the dept has been added.
More Struts CRUD examples. This one is Struts2 which has some powerful new features: http://www.jarvana.com/jarvana/view/org/apache/struts/struts2-assembly/2.0.14/struts2-assembly-2.0.14-all.zip!/struts-2.0.14/docs/docs/crud-demo-i.html
It is actually posted at Apache http://struts.apache.org/2.x/docs/crud-demo-i.html
Another struts 2: http://www.rkcole.com/articles/struts/crudTutorial/step1.html